package com.xbongbong.paas.script.controller;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.PaasStageEntity;
import com.xbongbong.paas.domain.entity.StageLogEntity;
import com.xbongbong.paas.domain.entity.StageProcessEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.elasticsearch.util.XbbElasticsearchRestTemplate;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.help.StageLogHelp;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.StageProcessModel;
import com.xbongbong.paas.model.StageWorkLogModel;
import com.xbongbong.paas.pojo.dto.FixDataJobDTO;
import com.xbongbong.paas.pojo.dto.FixGreenWorldDataJobDTO;
import com.xbongbong.paas.pojo.dto.FixGreenWorldStageJobDTO;
import com.xbongbong.paas.service.stage.StageHandle;
import com.xbongbong.paas.service.stage.StageHandleFactory;
import com.xbongbong.paas.service.stage.StageSettingService;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.CloneUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.config.ProBaseConfig;
import com.xbongbong.pro.constant.XbbProConstant;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.enums.PaasStageEnum;
import com.xbongbong.pro.enums.errorcodes.ClueErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.opportunity.pojo.dto.OpportunityUpdateBatchDTO;
import com.xbongbong.pro.opportunity.pojo.dto.OpportunityUpdateDTO;
import com.xbongbong.pro.script.pojo.dto.StageSettingScriptDTO;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.ClueStageEntity;
import com.xbongbong.saas.domain.entity.ClueStageLogEntity;
import com.xbongbong.saas.domain.entity.CustomerStageEntity;
import com.xbongbong.saas.domain.entity.CustomerStageLogEntity;
import com.xbongbong.saas.domain.entity.OpportunityStageLogEntity;
import com.xbongbong.saas.domain.entity.SaleStageEntity;
import com.xbongbong.saas.domain.entity.StageWorkEntity;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ClueEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.SalesOpportunityEnum;
import com.xbongbong.saas.help.StageHelp;
import com.xbongbong.saas.model.ClueModel;
import com.xbongbong.saas.model.ClueStageLogModel;
import com.xbongbong.saas.model.ClueStageModel;
import com.xbongbong.saas.model.ClueWorkLogModel;
import com.xbongbong.saas.model.CustomerModel;
import com.xbongbong.saas.model.CustomerStageLogModel;
import com.xbongbong.saas.model.CustomerStageModel;
import com.xbongbong.saas.model.CustomerWorkLogModel;
import com.xbongbong.saas.model.OpportunityModel;
import com.xbongbong.saas.model.OpportunityStageLogModel;
import com.xbongbong.saas.model.OpportunityWorkLogModel;
import com.xbongbong.saas.model.SaleStageModel;
import com.xbongbong.saas.model.StageWorkModel;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.sys.model.CompanyModel;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * @author guojun.hu
 * @version v1.0
 * @date 2021/11/18 15:15
 * @since v1.0
 */
@RestController
@RequestMapping(XbbProConstant.API_ROOT_PREFIX + "/script/stage")
public class StageScriptController {

    private static final Logger LOG = LoggerFactory.getLogger(StageScriptController.class);

    @Resource
    private ProBaseConfig proBaseConfig;
    @Resource
    private CustomerStageLogModel customerStageLogModel;
    @Resource
    private OpportunityStageLogModel opportunityStageLogModel;
    @Resource
    private ClueStageLogModel clueStageLogModel;
    @Resource
    private CustomerStageModel customerStageModel;
    @Resource
    private SaleStageModel saleStageModel;
    @Resource
    private ClueStageModel clueStageModel;
    @Resource
    private StageWorkModel stageWorkModel;
    @Resource
    private StageProcessModel stageProcessModel;
    @Resource
    private StageLogHelp stageLogHelp;
    @Resource
    private StageHelp stageHelp;
    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    @Resource
    private CustomerModel customerModel;
    @Resource
    private OpportunityModel opportunityModel;
    @Resource
    private ClueModel clueModel;
    @Resource
    private StageHandleFactory stageHandleFactory;
    @Resource
    private StageWorkLogModel stageWorkLogModel;
    @Resource
    private CustomerWorkLogModel customerWorkLogModel;
    @Resource
    private ClueWorkLogModel clueWorkLogModel;
    @Resource
    private OpportunityWorkLogModel opportunityWorkLogModel;
    @Resource
    private StageSettingService stageSettingService;
    @Resource
    private CompanyModel companyModel;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;

    /**
     * 当前会存在stage_log表有记录但是stage表没有阶段的情况（从来没有编辑过阶段）
     * 这种情况需要先用corpid=0的默认阶段初始化一份阶段存到stage表
     * 客户阶段处理
     */
    @RequestMapping(value = "/fixCustomerStage", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixCustomerStage(@RequestBody @Valid FixDataJobDTO fixDataJobDTO, BindingResult br) throws Exception {
        if (!proBaseConfig.getXbbCode().equals(fixDataJobDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
        }
        Long currentTime = DateTimeUtil.getInt();
        LOG.warn("开始处理客户阶段");
        List<CustomerStageLogEntity> customerStageLogList = customerStageLogModel.findStageLogWithoutStage();
        if (customerStageLogList.isEmpty()) {
            LOG.warn("没有需要处理的客户阶段数据，任务结束");
            return null;
        }
        // 获取corpid=0的客户阶段
        List<CustomerStageEntity> defaultCustomerStageList = customerStageModel.getDefaultList();
        // 获取corpid=0的客户阶段任务
        List<StageWorkEntity> defaultStageWorkList = stageWorkModel.getDefaultList(XbbRefTypeEnum.CUSTOMER_STAGE.getCode());
        // 用于批量插入的客户阶段数据
        List<CustomerStageEntity> insertCustomerStageList = new ArrayList<>();
        // 用于批量插入的客户阶段任务数据
        List<StageWorkEntity> insertStageWorkList = new ArrayList<>();
        // 用于打印执行进度的日志
        Integer totalCount = customerStageLogList.size();
        Integer currentCount = 0;
        for (CustomerStageLogEntity customerStageLog : customerStageLogList) {
            currentCount++;
            String corpid = customerStageLog.getCorpid();
            Long formId = customerStageLog.getFormId();
            LOG.warn("处理公司id{}和表单id{}的客户阶段数据，已经处理到第{}条数据，一共{}条数据", corpid, formId, currentCount, totalCount);
            List<CustomerStageEntity> cloneCustomerStageList = (List<CustomerStageEntity>) CloneUtil.deepClone(defaultCustomerStageList);
            for (CustomerStageEntity customerStage : cloneCustomerStageList) {
                customerStage.setId(null);
                customerStage.setCorpid(corpid);
                customerStage.setFormId(formId);
                customerStage.setAddTime(currentTime);
                customerStage.setUpdateTime(currentTime);
            }
            List<StageWorkEntity> cloneStageWorkList = (List<StageWorkEntity>) CloneUtil.deepClone(defaultStageWorkList);
            for (StageWorkEntity stageWork : cloneStageWorkList) {
                stageWork.setId(null);
                stageWork.setCorpid(corpid);
                stageWork.setFormId(formId);
                stageWork.setAddTime(currentTime);
                stageWork.setUpdateTime(currentTime);
            }
            insertCustomerStageList.addAll(cloneCustomerStageList);
            insertStageWorkList.addAll(cloneStageWorkList);
        }
        customerStageModel.insertBatch4Script(insertCustomerStageList);
        stageWorkModel.insertBatch(insertStageWorkList);
        LOG.warn("处理客户阶段完成，耗时{}", DateTimeUtil.getInt() - currentTime);
        return null;
    }

    /**
     * 当前会存在stage_log表有记录但是stage表没有阶段的情况（从来没有编辑过阶段）
     * 这种情况需要先用corpid=0的默认阶段初始化一份阶段存到stage表
     * 机会阶段处理
     */
    @RequestMapping(value = "/fixSaleStage", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixSaleStage(@RequestBody @Valid FixDataJobDTO fixDataJobDTO, BindingResult br) throws Exception {
        if (!proBaseConfig.getXbbCode().equals(fixDataJobDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
        }
        Long currentTime = DateTimeUtil.getInt();
        LOG.warn("开始处理机会阶段");
        List<OpportunityStageLogEntity> saleStageLogList = opportunityStageLogModel.findStageLogWithoutStage();
        if (saleStageLogList.isEmpty()) {
            LOG.warn("没有需要处理的机会阶段数据，任务结束");
            return null;
        }
        // 获取corpid=0的机会阶段
        List<SaleStageEntity> defaultSaleStageList = saleStageModel.getDefaultList();
        // 获取corpid=0的机会阶段任务
        List<StageWorkEntity> defaultStageWorkList = stageWorkModel.getDefaultList(XbbRefTypeEnum.SALE_STAGE.getCode());
        // 用于批量插入的机会阶段数据
        List<SaleStageEntity> insertSaleStageList = new ArrayList<>();
        // 用于批量插入的机会阶段任务数据
        List<StageWorkEntity> insertStageWorkList = new ArrayList<>();
        // 用于打印执行进度的日志
        Integer totalCount = saleStageLogList.size();
        Integer currentCount = 0;
        for (OpportunityStageLogEntity saleStageLog : saleStageLogList) {
            currentCount++;
            String corpid = saleStageLog.getCorpid();
            Long formId = saleStageLog.getFormId();
            LOG.warn("处理公司id{}和表单id{}的机会阶段数据，已经处理到第{}条数据，一共{}条数据", corpid, formId, currentCount, totalCount);
            List<SaleStageEntity> cloneSaleStageList = (List<SaleStageEntity>) CloneUtil.deepClone(defaultSaleStageList);
            for (SaleStageEntity saleStage : cloneSaleStageList) {
                saleStage.setId(null);
                saleStage.setCorpid(corpid);
                saleStage.setFormId(formId);
                saleStage.setAddTime(currentTime);
                saleStage.setUpdateTime(currentTime);
            }
            List<StageWorkEntity> cloneStageWorkList = (List<StageWorkEntity>) CloneUtil.deepClone(defaultStageWorkList);
            for (StageWorkEntity stageWork : cloneStageWorkList) {
                stageWork.setId(null);
                stageWork.setCorpid(corpid);
                stageWork.setFormId(formId);
                stageWork.setAddTime(currentTime);
                stageWork.setUpdateTime(currentTime);
            }
            insertSaleStageList.addAll(cloneSaleStageList);
            insertStageWorkList.addAll(cloneStageWorkList);
        }
        saleStageModel.insertBatch4Script(insertSaleStageList);
        stageWorkModel.insertBatch(insertStageWorkList);
        LOG.warn("处理机会阶段完成，耗时{}", DateTimeUtil.getInt() - currentTime);
        return null;
    }

    /**
     * 当前会存在stage_log表有记录但是stage表没有阶段的情况（从来没有编辑过阶段）
     * 这种情况需要先用corpid=0的默认阶段初始化一份阶段存到stage表
     * 线索阶段处理
     */
    @RequestMapping(value = "/fixClueStage", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixClueStage(@RequestBody @Valid FixDataJobDTO fixDataJobDTO, BindingResult br) throws Exception {
        if (!proBaseConfig.getXbbCode().equals(fixDataJobDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
        }
        Long currentTime = DateTimeUtil.getInt();
        LOG.warn("开始处理线索阶段");
        List<ClueStageLogEntity> clueStageLogList = clueStageLogModel.findStageLogWithoutStage();
        if (clueStageLogList.isEmpty()) {
            LOG.warn("没有需要处理的线索阶段数据，任务结束");
            return null;
        }
        // 获取corpid=0的线索阶段
        List<ClueStageEntity> defaultClueStageList = clueStageModel.getDefaultList();
        // 获取corpid=0的线索阶段任务
        List<StageWorkEntity> defaultStageWorkList = stageWorkModel.getDefaultList(XbbRefTypeEnum.CLUE_STAGE.getCode());
        // 用于批量插入的线索阶段数据
        List<ClueStageEntity> insertClueStageList = new ArrayList<>();
        // 用于批量插入的线索阶段任务数据
        List<StageWorkEntity> insertStageWorkList = new ArrayList<>();
        // 用于打印执行进度的日志
        Integer totalCount = clueStageLogList.size();
        Integer currentCount = 0;
        for (ClueStageLogEntity clueStageLog : clueStageLogList) {
            currentCount++;
            String corpid = clueStageLog.getCorpid();
            Long formId = clueStageLog.getFormId();
            LOG.warn("处理公司id{}和表单id{}的线索阶段数据，已经处理到第{}条数据，一共{}条数据", corpid, formId, currentCount, totalCount);
            List<ClueStageEntity> cloneClueStageList = (List<ClueStageEntity>) CloneUtil.deepClone(defaultClueStageList);
            for (ClueStageEntity clueStage : cloneClueStageList) {
                clueStage.setId(null);
                clueStage.setCorpid(corpid);
                clueStage.setFormId(formId);
                clueStage.setAddTime(currentTime);
                clueStage.setUpdateTime(currentTime);
            }
            List<StageWorkEntity> cloneStageWorkList = (List<StageWorkEntity>) CloneUtil.deepClone(defaultStageWorkList);
            for (StageWorkEntity stageWork : cloneStageWorkList) {
                stageWork.setId(null);
                stageWork.setCorpid(corpid);
                stageWork.setFormId(formId);
                stageWork.setAddTime(currentTime);
                stageWork.setUpdateTime(currentTime);
            }
            insertClueStageList.addAll(cloneClueStageList);
            insertStageWorkList.addAll(cloneStageWorkList);
        }
        clueStageModel.insertBatch4Script(insertClueStageList);
        stageWorkModel.insertBatch(insertStageWorkList);
        LOG.warn("处理线索阶段完成，耗时{}", DateTimeUtil.getInt() - currentTime);
        return null;
    }

    /**
     * 将版本V2数据迁移至V1启用的阶段，根据阶段名称匹配
     * 【阁润世界】要求将原本属于V2版本的数据迁移到V1版本上，对应的阶段通过阶段的名称进行匹配
     * 涉及到客户、机会、线索表单
     * <p>
     * 1. 获取V2版本关联的业务数据对应的阶段
     * 2. 通过阶段名称在V1版本匹配到对于阶段
     * 3. 更新业务数据的流程id、阶段id、阶段比例、阶段code
     * 4. 删除业务数据在V2版本上关联的阶段记录
     * 5. 生成业务数据在V1版本的阶段记录
     */
    @RequestMapping(value = "/fixGreenWorldStageProcess", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixGreenWorldStageProcess(@RequestBody @Valid FixGreenWorldDataJobDTO fixDataJobDTO, BindingResult br) throws Exception {
        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
        }
        if (!proBaseConfig.getXbbCode().equals(fixDataJobDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
        }
        List<Integer> validBusinessType = Arrays.asList(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode(), XbbRefTypeEnum.CLUE.getCode());
        Integer businessType = fixDataJobDTO.getBusinessType();
        if (Objects.isNull(businessType) || !validBusinessType.contains(businessType)) {
            throw new XbbException(ClueErrorCodeEnum.API_ERROR_1100100);
        }
        Integer saasMark = fixDataJobDTO.getSaasMark();
        String corpid = fixDataJobDTO.getCorpid();
        Long sourceStageProcessId = fixDataJobDTO.getSourceStageProcessId();
        Long targetStageProcessId = fixDataJobDTO.getTargetStageProcessId();
        StageProcessEntity sourceStageProcess = stageProcessModel.getByKey(sourceStageProcessId, corpid);
        StageProcessEntity targetStageProcess = stageProcessModel.getByKey(targetStageProcessId, corpid);
        if (Objects.isNull(sourceStageProcess) || Objects.isNull(targetStageProcess)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.STAGE_PROCESS_NOT_EXIST);
        }
        if (!Objects.equals(sourceStageProcess.getFormId(), targetStageProcess.getFormId())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, "需要覆盖的流程版本和用于覆盖的流程版本分属不同的表单");
        }
        Long formId = sourceStageProcess.getFormId();
        XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
        LOG.warn("开始处理{}阶段", xbbRefTypeEnum.getName());
        long currentTime = DateTimeUtil.getInt();

        List<PaasStageEntity> sourceStageList = stageHelp.getStageEntitys(corpid, saasMark, businessType, formId, sourceStageProcessId);
        List<String> sourceStageCodeList = sourceStageList.stream().map(PaasStageEntity::getCode).collect(Collectors.toList());
        List<PaasStageEntity> targetStageList = stageHelp.getStageEntitys(corpid, saasMark, businessType, formId, targetStageProcessId);
        Map<String, PaasStageEntity> sourceStageMap = sourceStageList.stream().filter(stage -> Objects.equals(stage.getEnable(), 1))
                .collect(Collectors.toMap(PaasStageEntity::getCode, Function.identity()));
        Map<String, PaasStageEntity> targetStageMap = targetStageList.stream().filter(stage -> Objects.equals(stage.getEnable(), 1))
                .collect(Collectors.toMap(PaasStageEntity::getName, Function.identity()));

        // 用于查询需要更新的数据
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpid));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(termQuery(StringConstant.FORM_ID, formId));
        if (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
            // boolQueryBuilder.filter(termQuery(CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.STAGE_PROCESS_ID), sourceStageProcessId));
            boolQueryBuilder.filter(termsQuery(CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.CUSTOMER_STAGE), sourceStageCodeList));
        } else if (Objects.equals(businessType, XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
            // boolQueryBuilder.filter(termQuery(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.STAGE_PROCESS_ID), sourceStageProcessId));
            boolQueryBuilder.filter(termsQuery(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.SALE_STAGE), sourceStageCodeList));
        } else {
            // boolQueryBuilder.filter(termQuery(ClueEnum.getEsAttr4Keyword(ClueEnum.STAGE_PROCESS_ID), sourceStageProcessId));
            boolQueryBuilder.filter(termsQuery(ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_STAGE), sourceStageCodeList));
        }
        boolQueryBuilder.filter(termsQuery(StringConstant.FLOW_STATUS, Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.getByCode(businessType).getIndex());
        searchRequest.source(sourceBuilder);
        CountRequest countRequest = new CountRequest(searchRequest.indices()[0]);
        countRequest.query(searchRequest.source().query());
        long totalCount = xbbElasticsearchRestTemplate.count(countRequest);
        LOG.warn("共需要处理{}条数据", totalCount);

        // 每1000条数据进行分页处理，计算分页页数
        int pageSize = 1000;
        int pageNum = (int) (totalCount % pageSize > 0 ? totalCount / pageSize + 1 : totalCount / pageSize);
        for (int currentPage = 1; currentPage <= pageNum; currentPage++) {
            int startIndex = (currentPage - 1) * pageSize;
            int endIndex = currentPage * pageSize;
            if (Objects.equals(currentPage, pageNum)) {
                endIndex = (int) totalCount;
            }
            LOG.warn("开始处理第{}——{}条数据", startIndex, endIndex);

            List<String> fieldList;
            if (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                fieldList = Arrays.asList(CustomerManagementEnum.CUSTOMER_STAGE.getAttr(), PaasStageEnum.STAGE_RATIO.getAttr(), PaasStageEnum.STAGE_ID.getAttr(), PaasStageEnum.STAGE_PROCESS_ID.getAttr());
            } else if (Objects.equals(businessType, XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
                fieldList = Arrays.asList(SalesOpportunityEnum.SALE_STAGE.getAttr(), PaasStageEnum.STAGE_RATIO.getAttr(), PaasStageEnum.STAGE_ID.getAttr(), PaasStageEnum.STAGE_PROCESS_ID.getAttr());
            } else {
                fieldList = Arrays.asList(ClueEnum.CLUE_STAGE.getAttr(), PaasStageEnum.STAGE_RATIO.getAttr(), PaasStageEnum.STAGE_ID.getAttr(), PaasStageEnum.STAGE_PROCESS_ID.getAttr());
            }
            EsUtil.setFieldList(sourceBuilder, fieldList);
            PageRequest pageRequest = EsUtil.setPage(sourceBuilder, currentPage, pageSize);
            searchRequest.source(sourceBuilder);
            XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
            if (esEntities == null) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
            }
            List<PaasFormDataEntityExt> dataList = esEntities.getContent();

            // 存放数据id 用于删除这些数据的关联stageLog
            List<Long> dataIdList = new ArrayList<>();
            // 存放需要更新阶段流程id 阶段id 阶段比例的数据
            List<OpportunityUpdateDTO> opportunityUpdateList = new ArrayList<>();
            // 切换新的版本后用于生成新的stageLog
            List<StageLogEntity> stageLogList = new ArrayList<>();
            for (PaasFormDataEntityExt formDataEntity : dataList) {
                Long dataId = formDataEntity.getDataId();
                String stageCode;
                if (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                    stageCode = FastJsonHelper.getStringOrDefaultFromFormData(formDataEntity.getData(), CustomerManagementEnum.CUSTOMER_STAGE.getAttr(), "");
                } else if (Objects.equals(businessType, XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
                    stageCode = FastJsonHelper.getStringOrDefaultFromFormData(formDataEntity.getData(), SalesOpportunityEnum.SALE_STAGE.getAttr(), "");
                } else {
                    stageCode = FastJsonHelper.getStringOrDefaultFromFormData(formDataEntity.getData(), ClueEnum.CLUE_STAGE.getAttr(), "");
                }
                PaasStageEntity sourceStage = sourceStageMap.get(stageCode);
                if (Objects.isNull(sourceStage)) {
                    LOG.info("id={}的数据的阶段code={}在源阶段流程{}中不存在", dataId, stageCode, sourceStageProcessId);
                    continue;
                }
                PaasStageEntity targetStage = targetStageMap.get(sourceStage.getName());
                if (Objects.isNull(targetStage)) {
                    LOG.info("id={}的数据的阶段code={}对应的阶段名称{}在目标阶段流程{}中不存在", dataId, stageCode, sourceStage.getName(), targetStageProcessId);
                    continue;
                }

                dataIdList.add(dataId);
                OpportunityUpdateDTO opportunityUpdateDTO = new OpportunityUpdateDTO();
                opportunityUpdateDTO.setId(dataId);
                opportunityUpdateDTO.setCorpid(corpid);
                JSONObject updateData = new JSONObject();
                updateData.put(PaasStageEnum.STAGE_PROCESS_ID.getAttr(), targetStage.getStageProcessId());
                updateData.put(PaasStageEnum.STAGE_ID.getAttr(), targetStage.getId());
                // 阶段比例这里简单处理，直接用阶段的进入比例
                updateData.put(PaasStageEnum.STAGE_RATIO.getAttr(), targetStage.getIntoProportion());
                // 更新阶段code
                if (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                    updateData.put(CustomerManagementEnum.CUSTOMER_STAGE.getAttr(), targetStage.getCode());
                } else if (Objects.equals(businessType, XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
                    updateData.put(SalesOpportunityEnum.SALE_STAGE.getAttr(), targetStage.getCode());
                } else {
                    updateData.put(ClueEnum.CLUE_STAGE.getAttr(), targetStage.getCode());
                }
                opportunityUpdateDTO.setData(updateData);
                opportunityUpdateList.add(opportunityUpdateDTO);

                StageLogEntity targetStageLog = initTargetStageLog(corpid, formId, dataId, targetStage);
                stageLogList.add(targetStageLog);
            }
            if (CollectionsUtil.isNotEmpty(opportunityUpdateList)) {
                OpportunityUpdateBatchDTO opportunityUpdateBatchDTO = new OpportunityUpdateBatchDTO();
                opportunityUpdateBatchDTO.setCorpid(corpid);
                opportunityUpdateBatchDTO.setOpportunityList(opportunityUpdateList);
                batchUpdateStageProcess(opportunityUpdateBatchDTO, businessType);
            }
            if (CollectionsUtil.isNotEmpty(dataIdList)) {
                // 批量删除业务数据在原来的阶段流程关联的stageLog
                stageLogHelp.deleteByStageProcessIdAndDataIdIn(dataIdList, corpid, formId, sourceStageProcessId, null, saasMark, businessType);
            }
            if (CollectionsUtil.isNotEmpty(stageLogList)) {
                // 切换流程版本之后批量生成stageLog
                stageLogHelp.insertBatchStageLogList(stageLogList, saasMark, businessType);
            }
        }

        LOG.warn("处理阶段完成，耗时{}", DateTimeUtil.getInt() - currentTime);
        return null;
    }

    /**
     * 初始化阶段记录
     *
     * @param corpid
     * @param formId
     * @param dataId
     * @param targetStage
     * @return
     */
    private StageLogEntity initTargetStageLog(String corpid, Long formId, Long dataId, PaasStageEntity targetStage) {
        StageLogEntity targetStageLog = new StageLogEntity();
        targetStageLog.setCorpid(corpid);
        targetStageLog.setFormId(formId);
        targetStageLog.setStageProcessId(targetStage.getStageProcessId());
        targetStageLog.setStageId(targetStage.getId());
        targetStageLog.setStageType(targetStage.getType());
        targetStageLog.setStageCode(targetStage.getCode());
        targetStageLog.setDataId(dataId);
        targetStageLog.setIsNow(1);
        // targetStageLog.setPreStageId();
        // targetStageLog.setBeforeStageCode();
        targetStageLog.setReasonId(0);
        targetStageLog.setMemo("");
        targetStageLog.setIntoTime(DateTimeUtil.getInt());
        targetStageLog.setOutTime(0L);
        // targetStageLog.setLastIntoTime();
        targetStageLog.setHistoryStayTime(0L);
        targetStageLog.setAddTime(DateTimeUtil.getInt());
        targetStageLog.setUpdateTime(DateTimeUtil.getInt());
        targetStageLog.setDel(0);
        return targetStageLog;
    }

    /**
     * 批量切换业务数据的流程版本
     *
     * @param opportunityUpdateBatchDTO
     * @param businessType
     * @throws XbbException
     */
    private void batchUpdateStageProcess(OpportunityUpdateBatchDTO opportunityUpdateBatchDTO, Integer businessType) throws XbbException {
        try {
            String corpid = opportunityUpdateBatchDTO.getCorpid();
            List<UpdateDataEntity> updateList = new ArrayList<>();
            List<OpportunityUpdateDTO> opportunityList = opportunityUpdateBatchDTO.getOpportunityList();
            if (CollectionsUtil.isNotEmpty(opportunityList)) {
                opportunityList.forEach(item -> updateList.add(ExplainUtil.getUpdateData(item.getId(), item.getData(), corpid)));
            }
            if (CollectionsUtil.isNotEmpty(updateList)) {
                if (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                    customerModel.updateBatch(updateList, corpid);
                } else if (Objects.equals(businessType, XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
                    opportunityModel.updateBatch(updateList, corpid);
                } else {
                    clueModel.updateBatch(updateList, corpid);
                }
            }
        } catch (Exception e) {
            LOG.error("StageScriptController.updateBatch报错:", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }

    /**
     * 将某个版本关联的处于“禁用状态”的阶段，根据名称匹配迁移至该版本“启用状态”的阶段
     * 【阁润世界】将V1版本数据“禁用状态”的阶段，根据名称匹配迁移至“启用状态”的阶段
     * 涉及到客户、机会、线索表单
     * <p>
     * 1. 获取V1版本禁用阶段关联的业务数据
     * 2. 更新业务数据的阶段id、阶段比例、阶段code
     * 3. 删除业务数据在禁用阶段上关联的阶段记录
     * 4. 生成业务数据在启用阶段的阶段记录
     */
    @RequestMapping(value = "/fixGreenWorldStage", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixGreenWorldStage(@RequestBody @Valid FixGreenWorldStageJobDTO fixStageJobDTO, BindingResult br) throws Exception {
        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
        }
        if (!proBaseConfig.getXbbCode().equals(fixStageJobDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
        }
        List<Integer> validBusinessType = Arrays.asList(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode(), XbbRefTypeEnum.CLUE.getCode());
        Integer businessType = fixStageJobDTO.getBusinessType();
        if (Objects.isNull(businessType) || !validBusinessType.contains(businessType)) {
            throw new XbbException(ClueErrorCodeEnum.API_ERROR_1100100);
        }
        Integer saasMark = fixStageJobDTO.getSaasMark();
        String corpid = fixStageJobDTO.getCorpid();
        Long disableStageId = fixStageJobDTO.getDisableStageId();
        Long enableStageId = fixStageJobDTO.getEnableStageId();
        Long stageProcessId = fixStageJobDTO.getStageProcessId();
        StageProcessEntity sourceStageProcess = stageProcessModel.getByKey(stageProcessId, corpid);
        if (Objects.isNull(sourceStageProcess)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.STAGE_PROCESS_NOT_EXIST);
        }
        StageHandle stageHandle = stageHandleFactory.getStageHandle(saasMark, businessType);
        PaasStageEntity disableStage = stageHandle.getByKey(disableStageId, corpid);
        PaasStageEntity enableStage = stageHandle.getByKey(enableStageId, corpid);
        if (Objects.isNull(disableStage) || Objects.isNull(enableStage)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, "disableStage或者enableStage为空");
        }
        Long formId = sourceStageProcess.getFormId();
        XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
        LOG.warn("开始处理{}阶段", xbbRefTypeEnum.getName());
        long currentTime = DateTimeUtil.getInt();

        // 用于查询需要更新的数据
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpid));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(termQuery(StringConstant.FORM_ID, formId));
        if (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
            // boolQueryBuilder.filter(termQuery(CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.STAGE_PROCESS_ID), stageProcessId));
            // boolQueryBuilder.filter(termQuery(CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.STAGE_ID), disableStageId));
            boolQueryBuilder.filter(termQuery(CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.CUSTOMER_STAGE), disableStage.getCode()));
        } else if (Objects.equals(businessType, XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
            // boolQueryBuilder.filter(termQuery(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.STAGE_PROCESS_ID), stageProcessId));
            // boolQueryBuilder.filter(termQuery(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.STAGE_ID), disableStageId));
            boolQueryBuilder.filter(termQuery(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.SALE_STAGE), disableStage.getCode()));
        } else {
            // boolQueryBuilder.filter(termQuery(ClueEnum.getEsAttr4Keyword(ClueEnum.STAGE_PROCESS_ID), stageProcessId));
            // boolQueryBuilder.filter(termQuery(ClueEnum.getEsAttr4Keyword(ClueEnum.STAGE_ID), disableStageId));
            boolQueryBuilder.filter(termQuery(ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_STAGE), disableStage.getCode()));
        }
        boolQueryBuilder.filter(termsQuery(StringConstant.FLOW_STATUS, Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.getByCode(businessType).getIndex());
        searchRequest.source(sourceBuilder);
        CountRequest countRequest = new CountRequest(searchRequest.indices()[0]);
        countRequest.query(searchRequest.source().query());
        long totalCount = xbbElasticsearchRestTemplate.count(countRequest);
        LOG.warn("共需要处理{}条数据", totalCount);

        // 每1000条数据进行分页处理，计算分页页数
        int pageSize = 1000;
        int pageNum = (int) (totalCount % pageSize > 0 ? totalCount / pageSize + 1 : totalCount / pageSize);
        for (int currentPage = 1; currentPage <= pageNum; currentPage++) {
            int startIndex = (currentPage - 1) * pageSize;
            int endIndex = currentPage * pageSize;
            if (Objects.equals(currentPage, pageNum)) {
                endIndex = (int) totalCount;
            }
            LOG.warn("开始处理第{}——{}条数据", startIndex, endIndex);

            List<String> fieldList = Arrays.asList(PaasStageEnum.STAGE_RATIO.getAttr(), PaasStageEnum.STAGE_ID.getAttr(), PaasStageEnum.STAGE_PROCESS_ID.getAttr());
            EsUtil.setFieldList(sourceBuilder, fieldList);
            PageRequest pageRequest = EsUtil.setPage(sourceBuilder, currentPage, pageSize);
            searchRequest.source(sourceBuilder);
            XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
            if (esEntities == null) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
            }
            List<PaasFormDataEntityExt> dataList = esEntities.getContent();

            // 存放数据id 用于删除这些数据在disableStage上的关联stageLog
            List<Long> dataIdList = new ArrayList<>();
            // 存放需要更新阶段id 阶段比例的数据
            List<OpportunityUpdateDTO> opportunityUpdateList = new ArrayList<>();
            // 切换新的阶段后用于生成新的stageLog
            List<StageLogEntity> stageLogList = new ArrayList<>();
            for (PaasFormDataEntityExt formDataEntity : dataList) {
                Long dataId = formDataEntity.getDataId();
                dataIdList.add(dataId);

                OpportunityUpdateDTO opportunityUpdateDTO = new OpportunityUpdateDTO();
                opportunityUpdateDTO.setId(dataId);
                opportunityUpdateDTO.setCorpid(corpid);
                JSONObject updateData = new JSONObject();
                updateData.put(PaasStageEnum.STAGE_PROCESS_ID.getAttr(), stageProcessId);
                updateData.put(PaasStageEnum.STAGE_ID.getAttr(), enableStageId);
                // 阶段比例这里简单处理，直接用阶段的进入比例
                updateData.put(PaasStageEnum.STAGE_RATIO.getAttr(), enableStage.getIntoProportion());
                if (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                    updateData.put(CustomerManagementEnum.CUSTOMER_STAGE.getAttr(), enableStage.getCode());
                } else if (Objects.equals(businessType, XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
                    updateData.put(SalesOpportunityEnum.SALE_STAGE.getAttr(), enableStage.getCode());
                } else {
                    updateData.put(ClueEnum.CLUE_STAGE.getAttr(), enableStage.getCode());
                }
                opportunityUpdateDTO.setData(updateData);
                opportunityUpdateList.add(opportunityUpdateDTO);

                StageLogEntity targetStageLog = initTargetStageLog(corpid, formId, dataId, enableStage);
                stageLogList.add(targetStageLog);
            }
            if (CollectionsUtil.isNotEmpty(opportunityUpdateList)) {
                OpportunityUpdateBatchDTO opportunityUpdateBatchDTO = new OpportunityUpdateBatchDTO();
                opportunityUpdateBatchDTO.setCorpid(corpid);
                opportunityUpdateBatchDTO.setOpportunityList(opportunityUpdateList);
                batchUpdateStageProcess(opportunityUpdateBatchDTO, businessType);
            }
            if (CollectionsUtil.isNotEmpty(dataIdList)) {
                // 批量删除业务数据在原来的阶段关联的stageLog
                stageLogHelp.deleteByStageProcessIdAndDataIdIn(dataIdList, corpid, formId, stageProcessId, disableStageId, saasMark, businessType);
            }
            if (CollectionsUtil.isNotEmpty(stageLogList)) {
                // 切换阶段之后批量生成stageLog
                stageLogHelp.insertBatchStageLogList(stageLogList, saasMark, businessType);
            }
        }

        LOG.warn("处理阶段完成，耗时{}", DateTimeUtil.getInt() - currentTime);
        return null;
    }
    /**
     * 统计关联数据多选、手签、富文本字段数量
     *
     * @param fixDataJobDTO
     * @param br
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/countFieldNum", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String countFieldNum(@RequestBody @Valid FixDataJobDTO fixDataJobDTO, BindingResult br) throws Exception {
        if (!proBaseConfig.getXbbCode().equals(fixDataJobDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
        }
        long currentTime = DateTimeUtil.getInt();
        List<String> corpidList = fixDataJobDTO.getCorpidList();
        if (CollectionsUtil.isEmpty(corpidList)) {
            corpidList = companyModel.getCorpidList();
        }
        LOG.info("数据库中共有公司{}家", corpidList.size());

        Set<String> linkDataMultiCorpidList = new HashSet<>();
        Set<String> manualSignCorpidList = new HashSet<>();
        Set<String> richTextCorpidList = new HashSet<>();
        Integer linkDataMultiNum = 0;
        Integer manualSignNum = 0;
        Integer richTextMultiNum = 0;
        Map<String, Object> param = new HashMap<>();
        param.put("del", 0);
        int totalCount = corpidList.size();
        int pageSize = Objects.isNull(fixDataJobDTO.getPageSize()) ? 500 : fixDataJobDTO.getPageSize();
        int pageNum = totalCount % pageSize > 0 ? totalCount / pageSize + 1 : totalCount / pageSize;
        for (int currentPage = 1; currentPage <= pageNum; currentPage++) {
            int startIndex = (currentPage - 1) * pageSize;
            int endIndex = currentPage * pageSize;
            if (Objects.equals(currentPage, pageNum)) {
                endIndex = totalCount;
            }
            LOG.info("执行进度第{}——{}个公司，一共{}个公司，进度{}/{}", startIndex, endIndex, totalCount, currentPage, pageNum);
            List<String> initCurrentCorpidList = corpidList.subList(startIndex, endIndex);
            param.put("corpidIn", initCurrentCorpidList);
            List<PaasFormExplainEntity> paasFormExplainEntityList = paasFormExplainModel.findEntitys(param);
            for (PaasFormExplainEntity paasFormExplainEntity : paasFormExplainEntityList) {
                String corpid = paasFormExplainEntity.getCorpid();
                List<FieldAttrEntity> fieldAttrEntityList = new ArrayList<>();
                try {
                    fieldAttrEntityList = JSON.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);
                } catch (Exception e) {
                    LOG.info("字段解释异常");
                }
                for (FieldAttrEntity fieldAttrEntity : fieldAttrEntityList) {
                    if (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.LINK_DATA_MULTI.getType())) {
                        linkDataMultiNum++;
                        linkDataMultiCorpidList.add(corpid);
                    } else if (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.MANUAL_SIGN.getType())) {
                        manualSignNum++;
                        manualSignCorpidList.add(corpid);
                    } else if (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.RICH_TEXT.getType())) {
                        richTextMultiNum++;
                        richTextCorpidList.add(corpid);
                    }
                }
            }
        }
        LOG.info("开启了关联数据多选字段的公司数量：{}", linkDataMultiCorpidList.size());
        LOG.info("开启了手签字段的公司数量：{}", manualSignCorpidList.size());
        LOG.info("开启了富文本字段的公司数量：{}", richTextCorpidList.size());
        LOG.info("关联数据多选字段一共{}个，手签字段一共{}个，富文本字段一共{}个", linkDataMultiNum, manualSignNum, richTextMultiNum);
        LOG.info("脚本耗时：{}", DateTimeUtil.getInt() - currentTime);
        return "success";
    }
}
